package com.dhq.myapp.util.http


import android.util.Log
import com.dhq.myapp.util.DataStorageUtils
import com.dhq.myapp.util.DataUtils
import com.dhq.myapp.util.LogUtil
import com.dhq.myapp.util.ProjectConstant
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import com.rokid.cxr.client.extend.sync.RetrofitClient
import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.ObservableTransformer
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.util.concurrent.TimeUnit

/**
 * DESC
 * Created by admin on 2017/3/28.
 */
class HttpUtil private constructor() {

    companion object {
        private val TAG = HttpUtil::class.java.simpleName
        private const val CONNECT_TIMEOUT = 15
        private const val READ_TIMEOUT = 30

        @Volatile
        private var mHttpUtil: HttpUtil? = null

        val instance: HttpUtil
            get() {
                if (mHttpUtil == null) {
                    synchronized(HttpUtil::class.java) {
                        if (mHttpUtil == null) {
                            mHttpUtil = HttpUtil()
                        }
                    }
                }
                return mHttpUtil!!
            }
    }

    private var retrofit: Retrofit? = null
    private var mApiService: ApiService? = null

    init {
        retrofitBuild()
    }

    /**
     * 初始化网络请求
     */
    private fun retrofitBuild() {
        //手动创建一个OkHttpClient并设置超时时间
        val httpClient = OkHttpClient.Builder()
            .connectTimeout(CONNECT_TIMEOUT.toLong(), TimeUnit.SECONDS)
            .readTimeout(READ_TIMEOUT.toLong(), TimeUnit.SECONDS)
            .addNetworkInterceptor(MyIntercepter())
            .build()

        val gsonConverterFactory = GsonConverterFactory.create(
            GsonBuilder().registerTypeAdapterFactory(object : TypeAdapterFactory {
                override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
                    val rawType = type.rawType as Class<T>
                    return if (rawType != String::class.java) {
                        null
                    } else {
                        //添加String类型null处理  如果为null改为空字符串返回
                        StringNullAdapter() as TypeAdapter<T>
                    }
                }
            }).create()
        )

        if (mApiService == null) {
            retrofit = Retrofit.Builder()
                .client(httpClient)
                .addConverterFactory(gsonConverterFactory)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(" https://baidu.com/ ")
                .build()
            mApiService = retrofit?.create(ApiService::class.java)
        }
    }

    /**
     * get请求 添加参数
     */
    fun getReq(url: String, paramMaps: HashMap<String, String>, observer: Observer<JsonElement>) {


        mApiService!!.getReq(getApiHost(url), paramMaps).compose(RxTransformer())
            .subscribe(observer)


    }

    /**
     * get请求 添加参数
     */
    fun getBiologyListRequest(
        url: String,
        paramMaps: HashMap<String, String>,
        observer: Observer<JsonElement>
    ) {

        mApiService!!.getBiologyListRequest(url, paramMaps).compose(RxTransformer())
            .subscribe(observer)
    }

    /**
     * 获取文件列表
     */
    fun getFileListRequest(
        url: String,
        paramMaps: String,
        observer: Observer<JsonElement>
    ) {

        val reqBody = RetrofitClient.createPartFromString(paramMaps)

        mApiService!!.getFileList(reqBody).compose(RxTransformer())
            .subscribe(observer)
    }

    /**
     * post请求 添加表单参数
     */
    fun postReq(url: String, paramMaps: HashMap<String, Any>, observer: BaseObserver<*>) {
        for (intercepter in ProjectConstant.intercepters) {
            intercepter.addParams(paramMaps)
        }

        LogUtil.d("请求地址：", getApiHost(url))
        LogUtil.d("请求参数：", DataUtils.mapToJson(paramMaps))

        mApiService?.postReq(getApiHost(url), paramMaps)?.compose(RxTransformer())
            ?.subscribe(observer)
    }

    /**
     * post请求 添加json参数
     */
    fun postJsonReq(
        url: String,
        paramMaps: HashMap<String, Any>,
        observer: BaseObserver<JsonElement>
    ) {
        val mediaType = "application/json;charset=utf-8".toMediaTypeOrNull()
        val jsonParam = DataUtils.mapToJson(paramMaps)
        val body = RequestBody.create(mediaType, jsonParam)

        LogUtil.d("请求地址：", getApiHost(url))
        LogUtil.d("请求参数：", jsonParam)
        mApiService?.postJsonReq(getApiHost(url), body)?.compose(RxTransformer())
            ?.subscribe(observer)
    }

    /**
     * 上传文件请求
     */
    fun uploadFileReq(
        url: String,
        files: List<File>,
        paramMaps: HashMap<String, String>?,
        observer: BaseObserver<JsonElement>
    ) {
        val builder = MultipartBody.Builder()

        if (files.isEmpty()) {
            Log.d(TAG, "必须添加一个文件")
            return
        }

        for (i in files.indices) {
            val file = files[i]
            addFile(builder, file, "pic$i")
        }

        paramMaps?.forEach { (key, value) ->
            builder.addFormDataPart(key, value)
        }

        val parts = builder.build().parts

        mApiService?.uploadFileReq(getApiHost(url), parts)?.compose(RxTransformer())
            ?.subscribe(observer)
    }

    /**
     * 上传文件请求
     */
    fun uploadFileReq(
        url: String,
        file: File,
        paramMaps: HashMap<String, Any>?,
        observer: BaseObserver<JsonElement>
    ) {
        for (intercepter in ProjectConstant.intercepters) {
//            intercepter.addParams(paramMaps)
        }

        val builder = MultipartBody.Builder()

        if (file == null) {
            Log.d(TAG, "必须添加一个文件")
            return
        }

        addImage(builder, file, "file")

        paramMaps?.forEach { (key, value) ->
            builder.addFormDataPart(key, value.toString())
        }

        val parts = builder.build().parts
        mApiService?.uploadFileReq(getApiHost(url), parts)?.compose(RxTransformer())
            ?.subscribe(observer)
    }

//    /**
//     * 下载文件请求
//     */
//    fun downLoadFileReq(url: String, paramMaps: HashMap<String, Any>?, observer: BaseDownLoadObserver) {
//        for (intercepter in ProjectConstant.intercepters) {
//            intercepter.addParams(paramMaps)
//        }
//
//        observer.showWaitingDialog()
//        observer.onStartDownload()
//        observer.fileName = url.substring(url.lastIndexOf(".") + 1)
//
//        mApiService?.downLoadFileGetReq(getApiHost(url), paramMaps)
//            ?.compose(RxDownTransformer())
//            ?.map(Function<ResponseBody, File> { responseBody ->
//                observer.saveFile(responseBody)
//            })
//            ?.observeOn(AndroidSchedulers.mainThread())
//            ?.subscribe(observer)
//    }

    /**
     * 添加文件 MultipartBody中添加文件
     */
    private fun addFile(
        builder: MultipartBody.Builder,
        file: File,
        paramName: String
    ): MultipartBody.Builder {
        val mediaType = "multipart/form-data".toMediaTypeOrNull()
        return addFile(builder, file, paramName, mediaType!!)
    }

    /**
     * 添加图片 MultipartBody中添加图片
     */
    private fun addImage(
        builder: MultipartBody.Builder,
        file: File,
        paramName: String
    ): MultipartBody.Builder {
        val mediaType = "image/jpeg".toMediaTypeOrNull()
        return addFile(builder, file, paramName, mediaType!!)
    }

    /**
     * 添加文件 MultipartBody中添加文件
     */
    private fun addFile(
        builder: MultipartBody.Builder,
        file: File,
        paramName: String,
        mediaType: MediaType
    ): MultipartBody.Builder {
        val imageBody = RequestBody.create(mediaType, file)
        builder.setType(MultipartBody.FORM)
            .addFormDataPart(paramName, file.name, imageBody)
        return builder
    }

    fun <T> toSubscribe(observable: Observable<T>, subscriber: Observer<T>) {
        observable
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber)
    }

    inner class RxTransformer<T> : ObservableTransformer<T, T> {
        override fun apply(observable: Observable<T>): ObservableSource<T> {
            return observable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
        }
    }

    inner class RxDownTransformer<T> : ObservableTransformer<T, T> {
        override fun apply(observable: Observable<T>): ObservableSource<T> {
            return observable
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .observeOn(Schedulers.computation())
        }
    }

    private fun getApiHost(url: String): String {
        return if (url.contains("http")) {
            url
        } else {
            val apiHost = DataStorageUtils.getStringValue("Host")
            apiHost + url
        }
    }
}